#define _GNU_SOURCE
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include <hwicodec_common.h>
#include <soc/soc_t20/jzm_jpeg_enc.h>

//static struct timeval encjpegstart,encjpegend;
static int hwicodec_pf_jpege_t20_validate_parameters(hwicodec_t *h)
{
	hwicodec_jpege_param_t *param = &h->param.param.jpege_param;

	if (param->i_devid > (T20_VPU_NUM - 1)) {
		hwicodec_log(h, C_LOG_ERROR, "i_devid:%d is invalidate\n", param->i_devid);
		goto err_i_devid;
	}

	if (param->i_csp != C_CSP_NV12 && param->i_csp != C_CSP_T420) {
		hwicodec_log(h, C_LOG_ERROR, "i_csp:%d is invalidate\n", param->i_csp);
		goto err_i_csp;
	}

	//if (param->i_mb_width < HWICODEC_MIN_MB_WIDTH || param->i_mb_height < HWICODEC_MIN_MB_HEIGHT) {
	if (param->i_mb_height < HWICODEC_MIN_MB_HEIGHT) {
		hwicodec_log(h, C_LOG_ERROR, "i_mb_resolution:%dx%d is invalidate\n",
				param->i_mb_width, param->i_mb_height);
		goto err_i_resolution;
	}

	return 0;

err_i_resolution:
err_i_csp:
err_i_devid:
	return -1;
}

static int hwicodec_pf_jpege_t20_init(hwicodec_t *h)
{
	int i = 0;
	hwicodec_vbinfo_t *dmavbinfo = NULL;
	struct channel_node cnode;
	hwicodec_soc_binfo_t binfo[] = {
		/* referrece && deblock */
		{256,	1024,	32,	32,	16,	16,	0,	256,	0},
		/* fenc */
		{256,	1024,	0,	0,	16,	16,	0,	256,	0},
		/* bit stream */
		{256,	0,		0,	0,	0,	0,	0,	0,		0},
		/* vconfig dma */
		{128,	0,		0,	0,	0,	0,	0,	0,		SOC_VDMA_SIZE},
	};

	if ((h->soc.fd = open(SOC_VPU_NAME, O_RDWR | O_SYNC | O_CLOEXEC)) < 0) {
		hwicodec_log(h, C_LOG_ERROR, "open %s failed\n", SOC_VPU_NAME);
		goto err_open_soc_vpu_name;
	}
	h->soc.vpu_num = T20_VPU_NUM;
	h->soc.cnpool.cn_num = 1;

	memset(&cnode, 0, sizeof(struct channel_node));
	cnode.mdelay = MDELAY_TIME;
	cnode.codecdir = HWJPEGENC;
	cnode.workphase = OPEN;
	cnode.thread_id = UNUSED_TID;
	if (ioctl(h->soc.fd, IOCTL_CHANNEL_REQ, &cnode) < 0) {
		hwicodec_log(h, C_LOG_ERROR, "request channel failed\n");
		goto err_request_channel;
	}

	for (i = 0; i < h->soc.cnpool.cn_num; i++) {
		h->soc.cnpool.chn[i].cnode = cnode;
		if ((h->soc.cnpool.chn[i].p = malloc(sizeof(_JPEGE_SliceInfo))) == NULL) {
			hwicodec_log(h, C_LOG_ERROR, "malloc the %d _JPEGE_SliceInfo failed\n", i);
			goto err_malloc_sliceinfo;
		}
	}

	if (hwicodec_pf_jpege_init_bpool(h, binfo) < 0) {
		hwicodec_log(h, C_LOG_ERROR, "init bpool failed\n");
		goto err_init_bpool;
	}

	if (h->param.param.jpege_param.b_tlb) {
		if (hwicodec_tlb(h, h->soc.bpool.buf_base_v, h->soc.bpool.buf_size, 0) < 0) {
			hwicodec_log(h, C_LOG_ERROR, "hwicodec tlb failed\n");
			goto err_hwicodec_tlb;
		}
		for (i = 0; i < h->soc.bpool.buf_num; i++) {
			h->soc.bpool.binfo[i].tlb_maped = 1;
		}
	}
	dmavbinfo = &h->soc.bpool.vbinfo[C_SOC_VB_DMA];
	if (dmavbinfo->num != 1) {
		hwicodec_log(h, C_LOG_ERROR, "request dma_num:%d, but alloc:%d\n", 1, dmavbinfo->num);
		goto err_dma_num;
	}
	for (i = 0; i < dmavbinfo->num; i++) {
		h->soc.cnpool.chn[i].cnode.dma_addr = h->param.param.jpege_param.b_tlb ? dmavbinfo->vb[i].yptr
			: h->param.ckvirt_to_phys(dmavbinfo->vb[i].yptr);
	}

	return 0;

err_dma_num:
err_hwicodec_tlb:
	hwicodec_pf_deinit_bpool(h);
err_init_bpool:
	i = h->soc.cnpool.cn_num;
err_malloc_sliceinfo:
	for (--i; i >= 0; i--) {
		free(h->soc.cnpool.chn[i].p);
		h->soc.cnpool.chn[i].p = NULL;
	}
	cnode.workphase = CLOSE;
	ioctl(h->soc.fd, IOCTL_CHANNEL_REL, &cnode);
err_request_channel:
	close(h->soc.fd);
err_open_soc_vpu_name:
	return -1;
	return 0;
}

static void hwicodec_pf_jpege_t20_deinit(hwicodec_t *h)
{
	int i = 0;

	hwicodec_pf_deinit_bpool(h);
	for (i = h->soc.cnpool.cn_num - 1; i >= 0; i--) {
		free(h->soc.cnpool.chn[i].p);
		h->soc.cnpool.chn[i].p = NULL;
	}
	h->soc.cnpool.chn[0].cnode.workphase = CLOSE;
	ioctl(h->soc.fd, IOCTL_CHANNEL_REL, &h->soc.cnpool.chn[0].cnode);
	close(h->soc.fd);
}

#ifndef ICODEC_HELIX_TEST
extern uint64_t timestamp_ivdc_vpu;
extern unsigned int overflow_cnt_vpu;
extern unsigned int data_threshold_vpu;
extern unsigned int ivdc_mem_line_vpu;
#endif
static int hwicodec_pf_jpege_t20_enc(hwicodec_t *h, hwicodec_io_t *input)
{
	hwicodec_jpege_param_t *param = &h->param.param.jpege_param;
	hwicodec_io_jpege_t *io = &input->protocol.jpege;
	hwicodec_chn_t *c = &h->soc.cnpool.chn[0];
	_JPEGE_SliceInfo *s = c->p;

    memset(s, 0, sizeof(_JPEGE_SliceInfo));

	s->raw_format = convert_csp_to_vpu_mode(param->i_csp);
	if (s->raw_format == HWICODEC_UNSUPPORT_MODE) {
		hwicodec_log(h, C_LOG_ERROR, "unsupported format to convert:%d\n", param->i_csp);
		goto err_unsupport_i_csp;
	}
	s->des_va = h->soc.bpool.vbinfo[C_SOC_VB_DMA].vb[0].yptr;
	s->des_pa = param->b_tlb ? s->des_va : h->param.ckvirt_to_phys(s->des_va);
	s->bsa = (uint8_t *)(param->b_tlb ? (intptr_t)io->fbs : h->param.ckvirt_to_phys((intptr_t)io->fbs));
	if ((s->raw_format == HWICODEC_NV12_MODE) || (s->raw_format == HWICODEC_NV21_MODE)) {
		if (io->fenc.yaddr & 1 << 31) {
			s->raw[0] = (uint8_t *)(io->fenc.yaddr);
			s->raw[1] = (uint8_t *)(io->fenc.caddr);
		} else {
			s->raw[0] = (uint8_t *)(param->b_tlb ? io->fenc.yaddr : h->param.ckvirt_to_phys(io->fenc.yaddr));
			s->raw[1] = (uint8_t *)(param->b_tlb ? io->fenc.caddr : h->param.ckvirt_to_phys(io->fenc.caddr));
		}
	} else if (s->raw_format == HWICODEC_TILE_MODE) {
		s->p0a = (uint8_t *)(param->b_tlb ? io->fenc.yaddr : h->param.ckvirt_to_phys(io->fenc.yaddr));
		s->p1a = (uint8_t *)(param->b_tlb ? io->fenc.caddr : h->param.ckvirt_to_phys(io->fenc.caddr));
	} else {
		hwicodec_log(h, C_LOG_ERROR, "unsupported format mode:%d\n", s->raw_format);
		goto err_unsupport_mode;
	}
	if (param->b_user_fenc && param->b_tlb && (hwicodec_vb_tlb(h, &h->soc.ppool, &io->fenc,
				&io->fenc_size, 0) < 0)) {
			hwicodec_log(h, C_LOG_ERROR, "hwicodec fenc tlb failed\n");
			goto err_hwicodec_fenc_tlb;
	}
	s->mb_width = param->i_mb_width;
	s->mb_height = param->i_mb_height;
	if (io->fenc.yaddr & 1 << 31) {
#ifndef ICODEC_HELIX_TEST
		s->stride[0] = s->stride[1] = 4096;
		c->cnode.frame_type = 3;
		c->cnode.overflow_cnt = overflow_cnt_vpu;
		c->cnode.ivdc_mem_line = ivdc_mem_line_vpu;
		c->cnode.data_threshold = data_threshold_vpu;
		c->cnode.time = timestamp_ivdc_vpu;
#endif
	} else {
		s->stride[0] = s->stride[1] = param->i_mb_width * 16;
	}
	s->nmcu = param->i_mb_width * param->i_mb_height - 1;
	s->nrsm = 0x0;
	s->ncol = 0x2;
	s->ql_sel = io->i_ql;
    if (io->user_ql_en) {
        s->qmem_table = io->qmem_table;
    } else {
        s->qmem_table = qmem_t20[s->ql_sel];
    }
    s->huffenc_sel = 0;
	s->frame_width = io->frame_width;
	s->frame_height = io->frame_width;

	T20_JPEGE_SliceInit(s);
    if (h->param.soc==C_T30 || h->param.soc==C_T21)
    {
        c->cnode.vpu_id = VPU_HELIX_ID | 1 << 0;
    }
    else if(h->param.soc==C_T20||h->param.soc==C_T10||h->param.soc==C_M200)
    {
        c->cnode.vpu_id = RANDOM_ID;
    }
#ifdef CONFIG_SINGLE_BS
	hwicodec_bsbuf_lock();
#endif

//	gettimeofday(&encjpegstart,NULL);
	if (ioctl(h->soc.fd, IOCTL_CHANNEL_RUN, &c->cnode) < 0) {
		hwicodec_log(h, C_LOG_ERROR, "vpu channel run failed\n");
		goto err_vpu_channel_run;
	}
//	gettimeofday(&encjpegend,NULL);
//	int timedt = (encjpegend.tv_sec*1000 + encjpegend.tv_usec/1000) - (encjpegstart.tv_sec*1000 + encjpegstart.tv_usec/1000);
//	printf("encode_jpeg time = %d\n",timedt);

	input->bslen = c->cnode.output_len;

	return 0;

err_vpu_channel_run:
err_hwicodec_fenc_tlb:
err_unsupport_mode:
err_unsupport_i_csp:
	return -1;
}

hwicodec_func_t hwicodec_pf_jpege_t20 = {
	.validate_parameters	= hwicodec_pf_jpege_t20_validate_parameters,
	.init					= hwicodec_pf_jpege_t20_init,
	.deinit					= hwicodec_pf_jpege_t20_deinit,
	.process				= hwicodec_pf_jpege_t20_enc,
	.request_vbinfo			= hwicodec_pf_request_vbinfo,
	.release_vbinfo			= hwicodec_pf_release_vbinfo,
};
